﻿using dy.net.job;
using dy.net.service;
using dy.net.utils;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Quartz;
using Serilog;
using Serilog.Events;
using Serilog.Filters;
using Serilog.Formatting.Compact;
using SqlSugar;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using System.IO.Compression;
using System.Net.Http;
using System.Reflection;
using System.Text;

namespace dy.net.extension
{
    public static class ServiceExtension
    {
        public class SwaggerOptions
        {
            public string Title { get; set; }
        }


        private static DbType GetDBType(IConfiguration configuration)
        {
            DbType dbType = DbType.Sqlite;
            var dbtypeString = configuration["dbtype"].ToLower();
            // 获取颜色枚举类型的所有枚举值
            var dbtypes = Enum.GetValues(typeof(DbType));
            foreach (DbType type in dbtypes)
            {
                if (type.ToString().ToLower() == dbtypeString)
                {
                    dbType = type;
                    break;
                }
            }

            return dbType;
        }

        private static string GetConnString(IConfiguration configuration, DbType dbType)
        {
            var connectionString = configuration["dbconn"];
            if (dbType == DbType.Sqlite)
            {
                connectionString = CreateSqliteDBConn();
            }
            return connectionString;
        }

        private static string CreateSqliteDBConn()
        {
            var fileFloder = Path.Combine(Environment.CurrentDirectory, "db");
            if (!Directory.Exists(fileFloder))
            {
                Directory.CreateDirectory(fileFloder);
            }
            var filePath = Path.Combine(fileFloder, "dy.sqlite");
            string conn = $"DataSource={filePath}";
            if (!File.Exists(filePath))
            {
                File.Create(filePath).Close();//.Close();
            }

            return conn;
        }


        /// <summary>
        /// 配置JWT认证
        /// </summary>
        public static void ConfigureJwtAuthentication(this IServiceCollection services)
        {
            var key = Encoding.ASCII.GetBytes(Md5Util.JWT_TOKEN_KEY);

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        context.Token = context.Request.Headers["Authorization"]
                                               .FirstOrDefault()?.Split(" ").Last();
                        return Task.CompletedTask;
                    }
                };
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.FromSeconds(60)
                };
            });
        }

        public static void AddSqlsugar(this IServiceCollection services, IConfiguration configuration)
        {
            DbType dbtype = GetDBType(configuration);
            services.AddScoped<ISqlSugarClient>(db =>
            {
                var sqlSugar = new SqlSugarClient(new ConnectionConfig
                {
                    ConnectionString = GetConnString(configuration, dbtype),
                    InitKeyType = InitKeyType.Attribute,
                    DbType = dbtype,
                    IsAutoCloseConnection = true // close connection after each operation (recommended)
                }, db =>
                {
                    //单例参数配置，所有上下文生效       
                    db.Aop.OnLogExecuting = (sql, pars) =>
                    {
                        //Serilog.Log.Debug(sql);//输出sql
                    };

                    db.Aop.OnError = (e) =>
                    {
                        Serilog.Log.Error(e.Message);
                        Serilog.Log.Error(e.Sql);
                    };

                    db.DbMaintenance.CreateDatabase(); // 检查数据库是否存在，如果不存在则创建
                    Assembly assembly = Assembly.GetExecutingAssembly(); // 获取当前程序集
                    Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型

                    var targetClasses = types
                        .Where(t => t.Namespace != null && t.Namespace.StartsWith("dy.net.model"))
                        .ToList();
                    db.CodeFirst.InitTables(targetClasses.ToArray());
                });

                return sqlSugar;
            });
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="services"></param>
        public static void AddQuartzService(this IServiceCollection services, IConfiguration configuration)
        {
            services.AddTransient<DouYinCollectSyncJob>();
            services.AddTransient<DouYinFavoritSyncJob>();
            services.AddTransient<DouYinUperPostSyncJob>();

            // 注册Quartz服务
            services.AddQuartz();

            services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);

            services.AddTransient<QuartzJobService>();
        }


        public static void AddHttpClients(this IServiceCollection services)
        {
            services.AddHttpClient("dy_collect", client =>
            {
                client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
                client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
                client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
            });


            services.AddHttpClient("dy_favorite", client =>
            {
                client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
            });

            services.AddHttpClient("dy_uper", client =>
            {
                //client.DefaultRequestHeaders.Referrer = new Uri("https://www.douyin.com/user/self?showTab=like");
            });
            // 配置HttpClient（Startup.cs或Program.cs）
            services.AddHttpClient("dy_download")
                .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
                {
                    // 控制并发连接数（根据服务器承受能力调整，建议5-20）
                    MaxConnectionsPerServer = 5,
                    // 禁用代理自动检测（减少不必要的延迟）
                    UseProxy = false,
                    // 连接超时（建立连接的超时时间）
                    ConnectTimeout = TimeSpan.FromSeconds(60)
                })
                // 配置客户端默认请求头
                .ConfigureHttpClient(client =>
                {
                    client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
                    client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
                    client.DefaultRequestHeaders.Add("Referer", "https://www.douyin.com");
                }); 
            //services.AddHttpClient("dy_down_fav")
            //  .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
            //  {
            //      // 控制并发连接数（根据服务器承受能力调整，建议5-20）
            //      MaxConnectionsPerServer = 5,
            //      // 禁用代理自动检测（减少不必要的延迟）
            //      UseProxy = false,
            //      // 连接超时（建立连接的超时时间）
            //      ConnectTimeout = TimeSpan.FromSeconds(60)
            //  });
            //services.AddHttpClient("dy_down_uper")
            //  .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
            //  {
            //      // 控制并发连接数（根据服务器承受能力调整，建议5-20）
            //      MaxConnectionsPerServer = 5,
            //      // 禁用代理自动检测（减少不必要的延迟）
            //      UseProxy = false,
            //      // 连接超时（建立连接的超时时间）
            //      ConnectTimeout = TimeSpan.FromSeconds(60)
            //  });
        }

        /// <summary>
        /// 自动注入指定命名空间下的所有服务
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <param name="namespace">目标命名空间（如 "MyApp.Services"）</param>
        /// <param name="assembly">服务所在的程序集（默认当前程序集）</param>
        /// <param name="includeSubNamespaces">是否包含子命名空间</param>
        public static IServiceCollection AddServicesFromNamespace(
            this IServiceCollection services,
            string @namespace,
            Assembly? assembly = null,
            bool includeSubNamespaces = false)
        {
            // 默认使用调用方的程序集
            assembly ??= Assembly.GetCallingAssembly();

            // 扫描程序集中指定命名空间的所有类
            var types = assembly.GetTypes()
                .Where(type =>
                    type.IsClass && // 只处理类
                    !type.IsAbstract && // 排除抽象类
                    !type.IsGenericTypeDefinition && // 排除泛型类
                    (includeSubNamespaces
                        ? type.Namespace?.StartsWith(@namespace, StringComparison.Ordinal) == true
                        : type.Namespace == @namespace) // 匹配命名空间（含子命名空间或精确匹配）
                );

            foreach (var type in types)
            {
                // 获取服务的生命周期（优先使用特性，默认 Transient）
                var lifetime = type.GetCustomAttribute<ServiceLifetimeAttribute>()?.Lifetime
                               ?? ServiceLifetime.Transient;

                // 查找该类实现的接口（优先注册为接口服务）
                var interfaces = type.GetInterfaces()
                    .Where(i => !i.IsGenericType || !i.GetGenericTypeDefinition().Equals(typeof(IDisposable)))
                    .ToList();

                if (interfaces.Any())
                {
                    // 注册：接口 -> 实现类（如 IUserService -> UserService）
                    foreach (var @interface in interfaces)
                    {
                        services.Add(new ServiceDescriptor(@interface, type, lifetime));
                    }
                }
                else
                {
                    // 无接口时，直接注册类本身（如 UserService 注册为自身）
                    services.Add(new ServiceDescriptor(type, type, lifetime));
                }
            }

            return services;
        }



        /// <summary>
        /// SwaggerUi
        /// </summary>
        /// <param name="app"></param>
        /// <param name="options"></param>
        public static void UseCustomSwaggerUI(this IApplicationBuilder app, Action<SwaggerOptions> options)
        {
            SwaggerOptions option = new SwaggerOptions();
            options?.Invoke(option);
            //启用中间件服务生成Swagger作为JSON终结点
            app.UseSwagger(c =>
            {
                //c.SerializeAsV2 = true;
                //c.RouteTemplate = "api-docs/{documentName}/swagger.json";
                c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
                {
                    swaggerDoc.Servers = new List<OpenApiServer> { new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}" } };
                    OpenApiPaths paths = new OpenApiPaths();
                    foreach (var path in swaggerDoc.Paths)
                    {
                        //if ( path.Key.StartsWith("/v1/api") )//做版本控制
                        paths.Add(path.Key, path.Value);
                    }
                    swaggerDoc.Paths = paths;
                });
            });
            //启用中间件服务对swagger-ui，指定Swagger JSON终结点
            app.UseSwaggerUI(c =>
            {
                //c.MaxDisplayedTags(5);
                //c.DisplayOperationId();//唯一标识操作
                c.SwaggerEndpoint("/swagger/v1/swagger.json", option.Title);
                //c.SwaggerEndpoint("/swagger/v2/swagger.json", "V2 Docs");
                c.RoutePrefix = "swagger";//根路由
                c.EnableDeepLinking();//启用深度链接--不知道干嘛的
                c.DisplayRequestDuration();//调试，显示接口响应时间
                c.EnableValidator();//验证
                c.DocExpansion(DocExpansion.List);//默认展开
                c.DefaultModelsExpandDepth(-1);//隐藏model
                c.DefaultModelExpandDepth(3);//model展开层级
                c.EnableFilter();//筛选--如果接口过多可以开启
                c.DefaultModelRendering(ModelRendering.Model);//设置显示参数的实体或Example
                //c.SupportedSubmitMethods(SubmitMethod.Get , SubmitMethod.Head , SubmitMethod.Post);//

                //c.OAuthClientId("test-id");
                //c.OAuthClientSecret("test-secret");
                //c.OAuthRealm("test-realm");
                //c.OAuthAppName("test-app");
                //c.OAuthScopeSeparator(" ");
                //c.OAuthAdditionalQueryStringParams(new Dictionary<string, string> { { "foo", "bar" } });
                //c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
            });
        }


        /// <summary>
        /// Swagger
        /// </summary>
        /// <param name="services"></param>
        public static IServiceCollection AddSwagger(this IServiceCollection services, Action<SwaggerGenOptions> options = null)
        {
            if (options != null)
                services.AddSwaggerGen(options);
            else
                services.AddSwaggerGen(DefaultSwaggerGenOptions());
            return services;
        }

        private static Action<SwaggerGenOptions> DefaultSwaggerGenOptions()
        {
            Action<SwaggerGenOptions> options = o =>
            {
                o.OperationFilter<SwaggerAuthorizationFilter>();

                o.SwaggerDoc("v1", new OpenApiInfo
                {
                    Version = "v1",
                    Title = "dy.net API Swagger Document",
                    //Description = "WebApi Swagger Document",

                    //TermsOfService = new Uri("https://ddnsapi.online.com"),
                    //Contact = new OpenApiContact
                    //{
                    //    Name = "剑之初",
                    //    Email = "xxxx@qq.com",
                    //    //Url = new Uri("https://ddns.online"),
                    //},
                    //License = new OpenApiLicense
                    //{
                    //    Name = "许可证",
                    //    Url = new Uri("https://ddns.online"),
                    //}
                });
                o.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.HttpMethod}");
                o.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                {
                    Description = "请在下方输入：Bearer {Token}",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                    BearerFormat = "JWT",
                    Scheme = "Bearer",
                });
                o.AddSecurityRequirement(new OpenApiSecurityRequirement
               {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer",
                            }
                        },
                        new[] { "readAccess", "writeAccess" }
                    }
               });

                o.DocumentFilter<SwaggerHiddenApiFilter>();
                var XmlPath = $"{AppContext.BaseDirectory}{AppDomain.CurrentDomain.FriendlyName}.xml";
                o.IncludeXmlComments(XmlPath);
                o.EnableAnnotations();
            };
            return options;
        }


        /// <summary>
        /// Serilog 日志拓展
        /// </summary>
        public static void ConfigureLogging(this WebApplicationBuilder builder)
        {
            string dateFile = "";// DateTime.Now.ToString("yyyyMMdd");

            Log.Logger = new LoggerConfiguration()
                //.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Is(LogEventLevel.Debug)
                .Enrich.FromLogContext()
                .Filter.ByExcluding(e => e.Level == LogEventLevel.Information) // 排除Info级别的日志
                .Filter.ByExcluding(Matching.FromSource("Microsoft"))
                .Filter.ByExcluding(Matching.FromSource("Quartz"))
                .WriteTo.Console(new RenderedCompactJsonFormatter(), LogEventLevel.Debug)
                //.WriteTo.MySQL(connectionString: builder.Configuration.GetConnectionString("DbConnectionString"), tableName: "Logs") // 输出到数据库
                .WriteTo.Logger(configure => configure
                    .Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Debug)
                    .WriteTo.File(
                        $"logs/log-debug-{dateFile}.txt",
                        rollingInterval: RollingInterval.Day,
                        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"))
                //.WriteTo.Logger(configure => configure
                //    .Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Information)
                //    .WriteTo.File(
                //        $"logs/log-info-{dateFile}.txt",
                //        rollingInterval: RollingInterval.Day,
                //        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"))
                .WriteTo.Logger(configure => configure
                    .Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Error)
                    .WriteTo.File(
                        $"logs/log-error-{dateFile}.txt",
                        rollingInterval: RollingInterval.Day,
                        outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"))
                //.WriteTo.File(
                //    $"logs/log-total-{dateFile}.txt",
                //    rollingInterval: RollingInterval.Day,
                //    outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
                //    restrictedToMinimumLevel: LogEventLevel.Debug)
                .CreateLogger();
        }

        /// <summary>
        /// 响应压缩
        /// 
        /// services.AddMyResponseCompression(); 需要配合 app.UseResponseCompression();
        /// </summary>
        /// <param name="services"></param>
        public static void AddMyResponseCompression(this IServiceCollection services)
        {

            // 第一步: 配置gzip与br的压缩等级为最优
            services.Configure<BrotliCompressionProviderOptions>(options =>
            {
                options.Level = CompressionLevel.Optimal;
            });

            services.Configure<GzipCompressionProviderOptions>(options =>
            {
                options.Level = CompressionLevel.Optimal;
            });

            // 第二步: 添加中间件
            services.AddResponseCompression(options =>
            {
                options.EnableForHttps = true;
                // 添加br与gzip的Provider
                options.Providers.Add<BrotliCompressionProvider>();
                options.Providers.Add<GzipCompressionProvider>();
                // 扩展一些类型 (MimeTypes中有一些基本的类型,可以打断点看看)
                options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
                {
                    "text/html; charset=utf-8",
                    "application/xhtml+xml",
                    "application/atom+xml",
                    "image/svg+xml",
                    "application/octet-stream"
                });
            });
        }
    }


    public class SwaggerAuthorizationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            operation.Parameters ??= new List<OpenApiParameter>();
            _ = context.ApiDescription.ActionDescriptor.AttributeRouteInfo;

            //先判断是否是匿名访问,
            if (context.ApiDescription.ActionDescriptor is ControllerActionDescriptor descriptor)
            {
                var Authorizes = descriptor.MethodInfo.GetCustomAttributes(typeof(AuthorizeFilter), true);
                //非匿名的方法,链接中添加accesstoken值
                if (Authorizes.Any())
                {
                    operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
                    //operation.Parameters.Add(new OpenApiParameter()
                    //{
                    //    Required = true,
                    //    Name = "Bearer",
                    //    In = ParameterLocation.Header,
                    //    Description = "You Must  Request With  token",
                    //    Style = ParameterStyle.DeepObject,

                    //});
                }
            }
        }
    }


    /// <summary>
    ///
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public partial class HiddenApiAttribute : Attribute { }

    /// <summary>
    ///
    /// </summary>
    public class SwaggerHiddenApiFilter : IDocumentFilter
    {
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            foreach (ApiDescription apiDescription in context.ApiDescriptions)
            {
                if (apiDescription.TryGetMethodInfo(out MethodInfo method))
                {
                    if (method.ReflectedType.CustomAttributes.Any(t => t.AttributeType == typeof(HiddenApiAttribute))
                            || method.CustomAttributes.Any(t => t.AttributeType == typeof(HiddenApiAttribute)))
                    {
                        string key = "/" + apiDescription.RelativePath;
                        if (key.Contains("?"))
                        {
                            int idx = key.IndexOf("?", StringComparison.Ordinal);
                            key = key.Substring(0, idx);
                        }
                        swaggerDoc.Paths.Remove(key);
                    }
                }
            }
        }
    }
}
